package han.chensing.latos.maker.windows.dialog.level;

import com.badlogic.gdx.Input;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Slider;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.TextField;
import com.badlogic.gdx.scenes.scene2d.utils.ArraySelection;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.OrderedSet;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.Function;

import han.chensing.latos.common.level.content.objects.CommonLine;
import han.chensing.latos.common.level.content.objects.CommonNote;
import han.chensing.latos.common.level.content.objects.CommonObject;
import han.chensing.latos.common.level.content.timeLine.frame.EditorEventTypes;
import han.chensing.latos.common.level.content.timeLine.frame.GeneralEventTypes;
import han.chensing.latos.common.level.util.bind_event.BindEventType;
import han.chensing.latos.common.level.util.bind_event.BindEventTypeExtra;
import han.chensing.latos.common.level.util.bind_event.BindEventTypeUtil;
import han.chensing.latos.maker.res.Res;
import han.chensing.latos.maker.V;
import han.chensing.latos.maker.function.AsString;
import han.chensing.latos.maker.latos.file.util.NoteParser;
import han.chensing.latos.maker.util.MusicStopWatch;
import han.chensing.latos.maker.windows.dialog.base.BaseDialog;
import han.chensing.latos.maker.windows.util.BatchEventTypesWidgetsAdd;
import han.chensing.latos.maker.windows.widget.CScrollList;
import han.chensing.latos.maker.windows.widget.CUIList;
import han.chensing.latos.maker.windows.widget.ShortCutWidget;
import han.chensing.latos.maker.windows.widget.line.SingleLineSimulatorWidget;
import han.chensing.latos.maker.windows.widget.line.SingleLineEditorWidget;

//@SuppressWarnings("unused")
public class LevelLineEditorDialog extends BaseDialog {

    @BindEventType(GeneralEventTypes.X) TextField tempLineXField;
    @BindEventType(GeneralEventTypes.Y) TextField tempLineYField;
    @BindEventType(GeneralEventTypes.Alpha) TextField tempLineAlphaField;
    @BindEventType(GeneralEventTypes.Rotate) TextField tempLineRotateField;
    @BindEventType(GeneralEventTypes.Speed) TextField tempLineSpeedField;
    @BindEventType(GeneralEventTypes.X) Label tempLineXLabel;
    @BindEventType(GeneralEventTypes.Y) Label tempLineYLabel;
    @BindEventType(GeneralEventTypes.Alpha) Label tempLineAlphaLabel;
    @BindEventType(GeneralEventTypes.Rotate) Label tempLineRotateLabel;
    @BindEventType(GeneralEventTypes.Speed) Label tempLineSpeedLabel;
    @BindEventType(GeneralEventTypes.X) TextButton resetTempLineX;
    @BindEventType(GeneralEventTypes.Y) TextButton resetTempLineY;
    @BindEventType(GeneralEventTypes.Alpha) TextButton resetTempLineAlpha;
    @BindEventType(GeneralEventTypes.Rotate) TextButton resetTempLineRotate;
    @BindEventType(GeneralEventTypes.Speed) TextButton resetTempLineSpeed;
    ShortCutWidget shortCutWidget;
    CScrollList<CommonLine> linesList;
    CScrollList<CommonNote> noteList;
    CScrollList<String> alignmentList;
    Slider progressSlider;
    TextButton playButton,pauseButton;
    CheckBox samplingModeCheck;
    CheckBox enableSoundCheck;
    Label shortcutHintLabel;
    SingleLineSimulatorWidget singleLineSimulatorWidget;
    SingleLineEditorWidget lineEditorWidget;

    MusicStopWatch musicStopWatch;

    ArrayList<String> alignmentsArrayList;

    int numberPressed=-1;

    public void debug_(){
    }

    public LevelLineEditorDialog() {
        super("Level editor");
        debug_();
        initializeAlignmentList();
        Skin skin = Res.get().get("skinBasic", Skin.class);
        setSize(2300,1350);

        musicStopWatch=new MusicStopWatch();

        linesList=new CScrollList<>(skin, CommonObject::getName);
        linesList.setData(V.project.getLevelData().getLineObjects());
        linesList.setBounds(10,20,70,1300);
        addActor(linesList);

        samplingModeCheck=new CheckBox("Sampling Mode",skin);
        samplingModeCheck.setBounds(1586,170,150,30);
        samplingModeCheck.setChecked(false);
        addActor(samplingModeCheck);

        enableSoundCheck=new CheckBox("Enable Sound",skin);
        enableSoundCheck.setBounds(1586,140,150,30);
        addActor(enableSoundCheck);

        AsString<CommonNote> noteAsString =
                t -> t.getStartTime() + " " +
                        t.getNoteType().toString() + " " +
                        t.getDistanceToLineCenter();
        noteList=new CScrollList<>(skin,
                noteAsString);
        noteList.setBounds(1266,170,300,1150);
        noteList.getCList().setMultiSelective(true);
        //addActor(noteList);

        progressSlider=new Slider(0,1,0.0001f,false,skin);
        progressSlider.setBounds(120,150,1066,10);
        addActor(progressSlider);

        playButton=new TextButton("Play",skin);
        playButton.setBounds(120,110,100,30);
        addActor(playButton);

        pauseButton=new TextButton("Pause",skin);
        pauseButton.setBounds(230,110,100,30);
        addActor(pauseButton);

        alignmentList=new CScrollList<>(skin,t->t);
        alignmentList.setBounds(1196,210,60,1110);
        alignmentList.setData(alignmentsArrayList);
        alignmentList.getCList().setMultiSelective(false);
        alignmentList.update();
        addActor(alignmentList);

        shortcutHintLabel=new Label("",skin);
        shortcutHintLabel.setBounds(1980,0,150,400);
        addActor(shortcutHintLabel);

        shortCutWidget=new ShortCutWidget();
        shortCutWidget.setBounds(1990,10,300,30);
        shortCutWidget.register(1001,"Cha",true, Input.Keys.B);
        shortCutWidget.register(1002,"Add",true);
        addActor(shortCutWidget);

        singleLineSimulatorWidget =new SingleLineSimulatorWidget();
        singleLineSimulatorWidget.setBounds(1266,749,1015,571);
        singleLineSimulatorWidget.init();
        addActor(singleLineSimulatorWidget);

        lineEditorWidget=new SingleLineEditorWidget();
        lineEditorWidget.setBounds(90,170,1100,1150);
        lineEditorWidget.init();
        addActor(lineEditorWidget);

        Function<Integer, Float> ySupplier = i -> 709f - 42f * i;
        BatchEventTypesWidgetsAdd.of(
                this,
                i->1266f, ySupplier,
                i->70f,i->30f,
                Label.class,
                BindEventTypeExtra.DEFAULT_EXTRA,
                skin,
                (index, fieldArg, theSkin) -> new Label(fieldArg, theSkin)
        ).start();
        BatchEventTypesWidgetsAdd.of(
                this,
                i->1266f+15+70f, ySupplier,
                i->200f,i->30f,
                TextField.class,
                BindEventTypeExtra.DEFAULT_EXTRA,
                skin,
                (index, fieldArg, theSkin) ->new TextField("", theSkin)
        ).start();
        BatchEventTypesWidgetsAdd.of(
                this,
                i->1566f, ySupplier,
                i->100f,i->30f,
                TextButton.class,
                BindEventTypeExtra.DEFAULT_EXTRA,
                skin,
                (index, fieldArg, theSkin) -> new TextButton(
                            String.format(Locale.getDefault(),"Reset %s",fieldArg),theSkin)
        ).start();
        BindEventTypeUtil.forEach(this, TextButton.class,BindEventTypeExtra.DEFAULT_EXTRA,
                (eventType,textButton) -> textButton.addListener(new ClickListener() {
                    @Override
                    public void clicked(InputEvent event, float x, float y) {
                        super.clicked(event, x, y);
                        BindEventTypeUtil.forSingle(
                                LevelLineEditorDialog.this, TextField.class,
                                eventType, BindEventTypeExtra.DEFAULT_EXTRA,
                                (eventType1, textField) ->
                                        textField.setText(String.valueOf(
                                                BindEventTypeUtil.getSingle(
                                                        lineEditorWidget,
                                                        float.class,
                                                        eventType,
                                                        SingleLineEditorWidget.TYPE_EXTRA_DEFAULT)
                                        ))
                        );
                    }
                }));
        BindEventTypeUtil.forEach(this, TextField.class,BindEventTypeExtra.DEFAULT_EXTRA,
                (eventType, textField) ->
            textField.addListener(new ChangeListener() {
                @Override
                public void changed(ChangeEvent event, Actor actor) {
                    BindEventTypeUtil.setInto(
                            lineEditorWidget,
                            float.class,
                            eventType,
                            SingleLineEditorWidget.TYPE_EXTRA_CURRENT,
                            (eventType1, field) -> {
                                String text = textField.getText();
                                float out = 0;
                                try {
                                    out = Float.parseFloat(text);
                                } catch (Exception ignore) {}
                                return out;
                            }
                    );
                }
            }));

        moveToCenter();

        updateLinesList();
        updateShortcutHintLabel();

        samplingModeCheck.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                updateShortcutHintLabel();
            }
        });
        linesList.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                onLineSelected();
            }
        });
        noteList.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                updateNoteInfo();
            }
        });
        noteList.addListener(new ClickListener(){
            @Override
            public void clicked(InputEvent event, float x, float y) {
                super.clicked(event, x, y);
                updateNoteInfo();
            }
        });
        playButton.addListener(new ClickListener(){
            @Override
            public void clicked(InputEvent event, float x, float y) {
                super.clicked(event, x, y);
                Music music = V.project.getMusic();
                musicStopWatch.forceUpdateNowTime(music);
                musicStopWatch.setPaused(false);
                music.play();
            }
        });
        pauseButton.addListener(new ClickListener(){
            @Override
            public void clicked(InputEvent event, float x, float y) {
                super.clicked(event, x, y);
                Music music = V.project.getMusic();
                musicStopWatch.forceUpdateNowTime(music);
                musicStopWatch.setPaused(true);
                music.pause();
            }
        });
        progressSlider.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                if (progressSlider.isDragging()) {
                    Music music = V.project.getMusic();
                    musicStopWatch.forceUpdateNowTime(music);
                    musicStopWatch.setPaused(true);
                    music.play();
                    music.setPosition(
                            V.project.getMusicLength() * progressSlider.getValue());
                    music.pause();
                }
            }
        });
        addListener(new InputListener(){
            @Override
            public boolean keyUp(InputEvent event, int keycode) {
                boolean typeMode = shortCutWidget.isKeyPressed(1001);
                switch (keycode) {
                    case Input.Keys.NUM_1, Input.Keys.NUM_2, Input.Keys.NUM_3, Input.Keys.NUM_4 -> {
                        numberPressed = -1;
                        if (!typeMode)
                            shortCutWidget.reverseKeyPressed(1002);
                        //lineEditorWidget.setNotePlacing(-1);
                    }
                }
                return super.keyUp(event, keycode);
            }

            @Override
            public boolean keyDown(InputEvent event, int keycode) {
                boolean typeMode = shortCutWidget.isKeyPressed(1001);
                switch (keycode) {
                    case Input.Keys.FORWARD_DEL, Input.Keys.DEL ->
                        //lineEditorWidget.deleteNotes();
                            updateNoteList();
                    case Input.Keys.NUM_1 -> {
                        numberPressed = 1;
                        if (typeMode) {
                            changeTypeOfSelectedKeys("Tap");
                            break;
                        }
                        shortCutWidget.reverseKeyPressed(1002);
                    }
                    case Input.Keys.NUM_2 -> {
                        numberPressed = 2;
                        if (typeMode) {
                            changeTypeOfSelectedKeys("Drag");
                            break;
                        }
                        shortCutWidget.reverseKeyPressed(1002);
                    }
                    case Input.Keys.NUM_3 -> {
                        numberPressed = 3;
                        if (typeMode) {
                            changeTypeOfSelectedKeys("Hold");
                            break;
                        }
                        shortCutWidget.reverseKeyPressed(1002);
                    }
                    case Input.Keys.NUM_4 -> {
                        numberPressed = 4;
                        if (typeMode) {
                            changeTypeOfSelectedKeys("Flick");
                            break;
                        }
                        shortCutWidget.reverseKeyPressed(1002);
                    }
                    case Input.Keys.LEFT -> {
                        int step = 10;
                        if (shortCutWidget.isKeyPressed(0))
                            step = 30;
                        else if (shortCutWidget.isKeyPressed(2))
                            step = 1;
                        int finalStep = step;
                        for (CUIList.CUIListItem<CommonNote> noteCUIListItem : noteList.getCList().getSelection().items()) {
                            CommonNote value = noteCUIListItem.getValue();
                            value.setDistanceToLineCenter(value.getDistanceToLineCenter() - finalStep);
                        }
                    }
                    case Input.Keys.RIGHT -> {
                        int step = 10;
                        if (shortCutWidget.isKeyPressed(0))
                            step = 30;
                        else if (shortCutWidget.isKeyPressed(2))
                            step = 1;
                        int finalStep = step;
                        for (CUIList.CUIListItem<CommonNote> noteCUIListItem : noteList.getCList().getSelection().items()) {
                            CommonNote value = noteCUIListItem.getValue();
                            value.setDistanceToLineCenter(value.getDistanceToLineCenter() + finalStep);
                        }
                    }
                }
                return super.keyDown(event, keycode);
            }
        });
    }

    private void initializeAlignmentList(){
        alignmentsArrayList=new ArrayList<>();
        alignmentsArrayList.add("None");
        alignmentsArrayList.add("1/1");
        alignmentsArrayList.add("1/2");
        alignmentsArrayList.add("1/3");
        alignmentsArrayList.add("1/4");
        alignmentsArrayList.add("1/6");
        alignmentsArrayList.add("1/8");
        alignmentsArrayList.add("1/12");
        alignmentsArrayList.add("1/16");
        alignmentsArrayList.add("1/24");
        alignmentsArrayList.add("1/32");
        alignmentsArrayList.add("Custom");
    }

    private void changeTypeOfSelectedKeys(String type){
        /*String text = noteInfoField.getText();
        String type1 = RegexUtil.replaceField(text, "type", type);
        noteInfoField.setText(type1);
        applyNoteInfo(type1);*/
    }

    private void updateShortcutHintLabel() {
        shortcutHintLabel.setAlignment(Align.left);
        shortcutHintLabel.setText(samplingModeCheck.isChecked() ?
                        "Mouse Left:Drag progress\n" +
                        "Mouse Right: Select Notes\n" +
                        "Mouse Middle:Drag scale\n" +
                        "Num+Mouse Left:Add Note\n" +
                        "  1:Tap\n" +
                        "  2:Drag\n" +
                        "  3:Hold\n" +
                        "  4:Flick\n" +
                        "Del:Delete selected Notes\n" +
                        "B:Edit Type of selected note\n" +
                        "  B+1:Tap\n" +
                        "  B+2:Drag\n" +
                        "  B+3 Hold\n" +
                        "  B+4 Flick\n" +
                        "Left&Right:Move selected Notes\n" +
                        "(Ctrl to move more, Alt to move less)\n" :
                        "Mouse Left:Drag progress\n" +
                        "Mouse Right:Select Notes\n" +
                        "Mouse Middle:Drag scale\n" +
                        "B:Edit Type of selected note\n" +
                        "  B+1:Tap\n" +
                        "  B+2:Drag\n" +
                        "  B+3 Hold\n" +
                        "  B+4 Flick\n" +
                        "Left&Right:Move selected Notes\n" +
                        "(Ctrl to move more, Alt to move less)\n" );
    }

    public void updateLinesList(){
        linesList.update();
    }

    private void onLineSelected(){
        updateNoteList();
        updateSimulatorLine();
    }

    private void updateSimulatorLine(){
        int selectedIndex = linesList.getCList().getSelectedIndex();
        singleLineSimulatorWidget.setCurrentLineIndex(selectedIndex);
        lineEditorWidget.setCurrentLineIndex(selectedIndex);
    }

    private void updateSimulatorNowTime(){
        musicStopWatch.updateNowTime(V.project.getMusic());
        float nowTime = musicStopWatch.getNowTime();
        singleLineSimulatorWidget.setNowTime(nowTime);
        lineEditorWidget.setNowTime(nowTime);
    }

    private void updateNoteList(){
        int selectedIndex = linesList.getCList().getSelectedIndex();
        if (selectedIndex<0)
            return;
        CommonLine commonLine = V.project.getLevelData().getLineObjects().get(selectedIndex);
        noteList.setData(commonLine.getNotes());
        noteList.update();
    }

    private void updateNoteInfo(){//TODO Needs modify
        String info = NoteParser.infoFromNotes(noteList.getCList().getSelection().items());
    }

    public void updateSelectedNotes(List<CommonNote> commonNotes){
        ArraySelection<CUIList.CUIListItem<CommonNote>> selection = noteList.getCList().getSelection();
        OrderedSet<CUIList.CUIListItem<CommonNote>> items = selection.items();
        selection.clear();
        for (CommonNote commonNote : commonNotes) {
            CUIList.CUIListItem<CommonNote> noteCUIListItem =new CUIList.CUIListItem<>();
            noteCUIListItem.setValue(commonNote);
            //selection.choose(noteCListItem);
            items.add(noteCUIListItem);
        }
        updateNoteInfo();
        selection.validate();
        noteList.invalidateHierarchy();
    }

    public String getCustomAlignmentValue(){
        return "";
    }

    private void applyNoteInfo(String info){
        try {
            NoteParser.infoToNotes(noteList.getCList().getSelection().items(), info);
        }catch (Exception ignore){}
        updateNoteInfo();
        updateNoteList();
    }

    @Override
    public void draw(Batch batch, float parentAlpha) {
        super.draw(batch, parentAlpha);
        if (progressSlider!=null&&!progressSlider.isDragging())
            progressSlider.setValue(V.project.getMusic().getPosition() / V.project.getMusicLength());
        updateSimulatorNowTime();
    }
}
